/*
 * Copyright 2014-2026 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.wzq.http;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.TreeMap;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

import org.apache.http.HttpEntity;
import org.apache.http.HttpException;
import org.apache.http.HttpHeaders;
import org.apache.http.HttpRequest;
import org.apache.http.HttpRequestInterceptor;
import org.apache.http.NameValuePair;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.protocol.HttpContext;
import org.apache.http.util.EntityUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Value;

import com.wzq.util.ApiError;
import com.wzq.util.ApiException;
import com.wzq.util.UtilDate;
import com.wzq.util.UtilHttp;
import com.wzq.util.UtilIo;
import com.wzq.util.UtilJson;
import com.wzq.util.UtilString;



public class ApiHttpClient {
	protected final Logger logger = LogManager.getLogger(getClass());
	
	/** 内部调用httpClient */
	private CloseableHttpClient httpClient;
	@Value("${client.maxConnPerRoute:100}")
	private Integer maxConnPerRoute = 100;
	@Value("${client.maxConnTotal:2000}")
	private Integer maxConnTotal = 2000;
	@Value("${server.appId:}")
	private String appId;
	@Value("${client.connectTimeout:3000}")
	private Integer connectTimeout = 3000;
	@Value("${client.socketTimeout:30000}")
	private Integer socketTimeout = 30000; 
	/**
	 * 字符集
	 */
	private final String CHAR_SET="UTF-8";
	
	public String getAppId() {
		return appId;
	}
	public void setAppId(String appId) {
		this.appId = appId;
	}

	public Integer getMaxConnTotal() {
		return maxConnTotal;
	}
	public void setMaxConnTotal(Integer maxConnTotal) {
		this.maxConnTotal = maxConnTotal;
	}
	public Integer getSocketTimeout() {
		return socketTimeout;
	}
	public void setSocketTimeout(Integer socketTimeout) {
		this.socketTimeout = socketTimeout;
	}
	@PostConstruct
	public void init() {
		logger.info("appId={}, 初始化http client服务……", appId);
		if (appId == null || appId.isEmpty()) throw new IllegalStateException("app.properties未配置 server.appId=???");
		
		HttpClientBuilder builder = HttpClientBuilder.create();
		builder.setMaxConnPerRoute(maxConnPerRoute).setMaxConnTotal(maxConnTotal);
		builder.setDefaultRequestConfig(RequestConfig.custom()
				.setConnectTimeout(connectTimeout).setSocketTimeout(socketTimeout).build());
		builder.addInterceptorFirst(new HttpRequestInterceptor() {
			private String appIdStr = appId.toString();
			
			@Override
			public void process(HttpRequest request, HttpContext context) throws HttpException, IOException {
//				request.setHeader(EyeFilter.rpcTrackName, UtilJson.writeValueAsString(UtilEye.getTrackInfo())); // send
				request.addHeader("appId", appIdStr);
				String timestamp = System.currentTimeMillis() + "";
				request.addHeader("timestamp", timestamp);
			}
		});
		httpClient = builder.build();
	}
	
	@PreDestroy
	public void destroy() {
		logger.info("appId={}, 卸载http client服务……", appId);
		UtilIo.close(httpClient);
	}

	
	@SuppressWarnings("unchecked")
	private ApiError toApiError(Integer statusCode, String html) {
		Map<String, Object> json = UtilJson.toMap(html);
		ApiError apiError = new ApiError();
		if (json.containsKey("_error")) {
			apiError.getValues().putAll((Map<String, Object>) json.get("_error"));
		} else {
			apiError.getValues().putAll(json);
		}
		if (apiError.getHttpCode() == null) apiError.setHttpCode(statusCode);
		return apiError;
	}
	
	private String execute(HttpUriRequest request, Map<String, Object> params) throws ApiException {
		long start = System.currentTimeMillis();
		String html = null;
		int statusCode = 418;
		logger.trace("http请求：{}", request);
		CloseableHttpResponse response=null;
		try {
		    response = httpClient.execute(request);
			statusCode = response.getStatusLine().getStatusCode();
			html = EntityUtils.toString(response.getEntity(),CHAR_SET);
			long end = System.currentTimeMillis();
			logger.trace("http结果：{}, {}, {}", UtilIo.prettyByte(html.length()), UtilDate.prettyTime(start, end), UtilString.substring(html, 0, 1024));
		} catch (IOException e) {
			html = UtilString.substring(html, 0, 1024);
			long end = System.currentTimeMillis();
			logger.warn("{}, {}, params={}, http={}, html={}, ex={}", request, UtilDate.prettyTime(start, end), params, statusCode, html, e);
			ApiError apiError = new ApiError();
			apiError.setHttpCode(statusCode);
			apiError.setMsg("异常：" + e + "，html:" + html);
			request.abort();
			throw new ApiException(apiError);
		}finally{
		    if(response != null){
		        try {
                    response.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
		    }
		}
		if (statusCode == 200) {
			if (html.indexOf("\"_error\":{\"") != -1) throw new ApiException(toApiError(200, html));
			return html;
		}
		
		if (html == null || !html.startsWith("{") || !html.endsWith("}") ) {
			ApiError apiError = new ApiError();
			apiError.setHttpCode(statusCode);
			apiError.setMsg("html: " + html);
			request.abort();
			throw new ApiException(apiError);
		}
		throw new ApiException(toApiError(statusCode, html));
	}
	
	
	@SuppressWarnings("unchecked")
	public String get(String url, Object params) throws ApiException {
		Map<String, Object> map = params == null ? null : params instanceof Map ? (Map<String, Object>) params : UtilJson.toMap(params);
		HttpGet request = map == null || map.isEmpty() ? new HttpGet(url) : new HttpGet(UtilHttp.toURI(url, map));
		return execute(request, map);
	}
	
	/**
	 * 通过get方式获取数据，自动签名params
	 * @param url
	 * @param params key1, value1, ... , keyN, valueN
	 * @return 执行结果
	 */
	public String get(String url, Object... params) throws ApiException {
		Map<String, Object> data = new TreeMap<>();
		for (int i = 0, len = params.length; i < len;) {
			data.put(UtilString.toString(params[i++]), params[i++]);
		}
		return get(url, data);
	}
	
	public String post(String url, Object params) throws ApiException {
		HttpPost post = new HttpPost(url);
		@SuppressWarnings("unchecked")
		Map<String, Object> map = params == null ? null : params instanceof Map ? (Map<String, Object>) params : UtilJson.toMap(params);
		if (map == null || map.isEmpty()) return execute(post, map);
		
		List<NameValuePair> list = new ArrayList<>(map.size());
		for (Entry<String, Object> entry : map.entrySet()) {
			list.add(new BasicNameValuePair(
					UtilString.toString(entry.getKey(), ""),
					UtilString.toString(entry.getValue(), "")));
		}
		post.setEntity(new UrlEncodedFormEntity(list, UtilIo.utf8));
		return execute(post, map);
	}
	public String post(String url, Object... params) {
		Map<String, Object> data = new TreeMap<>();
		for (int i = 0, len = params.length; i < len;) {
			data.put(UtilString.toString(params[i++]), params[i++]);
		}
		return post(url, data);
	}
	
	public String post(String uri, HttpEntity entity) {
		HttpPost post = new HttpPost(uri);
		post.setEntity(entity);
		return execute(post, null);
	}
	
	public String post(String uri, HttpEntity entity,Map<String,String> header){
	    HttpPost post = new HttpPost(uri);
	    if(header != null){
	        for(Map.Entry<String,String> head:header.entrySet()){
	            post.addHeader(head.getKey(), head.getValue());
	        }
	    }
        post.setEntity(entity);
        return execute(post, null);
	}
	
	public String postJson(String uri, HttpEntity entity){
	    Map<String,String> header = new HashMap<String,String>();
	    header.put(HttpHeaders.CONTENT_TYPE,"application/json");
        return post(uri,entity,header);
    }
}
